/*
 * smart_home.cpp
 *
 * Copyright (c) 2012-2012 Baghdasaryan Andrew (relfock at gmail dot com)
 */
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <string>
#include <iostream>

#include <signal.h>

#include "tty_io.hpp"
#include "helper.hpp"
#include "udp_server.hpp"
#include "tcp_server.hpp"
#include "fingerprint.hpp"
#include "config_parser.hpp"

#define TTY_CONF_NAME "tty.conf"
#define XML_CONFIG_NAME "config.xml"
#define USER_XML_CONFIG_NAME "user_access.xml"

/* system sleep */
SysSleep sys_sleep;

/* mutex for saving config */
pthread_mutex_t save_config_mutex;

/* tty-fd-mutex map */
std::map<std::string, Serial_info> port_map;

/* global_id objects map */
std::map<unsigned int, Base*> gids;

/* global_id per class map */
std::map<unsigned int, Base> vars;
std::map<unsigned int, Mode> modes;
std::map<unsigned int, Panel> panels;
std::map<unsigned int, Gsm> gsm_cmds;
std::map<unsigned int, Sensor> sensors;
std::map<unsigned int, Command> commands;
std::map<unsigned int, Timer> user_timers;
std::map<unsigned int, Fingerprint> fingerprint_cmds;
std::map<unsigned int, Function> functions;

/* mutex for logging */
pthread_mutex_t logger_mutex;

xmlpp::DomParser user_xml_doc;

void signal_handler(int sig)
{
    pthread_mutex_lock(&save_config_mutex);
    logger(SM_LOG_INFO, "Smart Home server stopped"); 
    exit(0);
}

/* Main entry point of the Smart home daemon. */
int main(int argc, char **argv)
{   
    sys_sleep.gid = 0;
    sys_sleep.name = "sleep";
    gids[sys_sleep.gid] = &sys_sleep;

    pthread_mutex_init(&logger_mutex, NULL);
    pthread_mutex_init(&save_config_mutex, NULL); 
    try {
        /* Register signal handlers. */
        signal(SIGTERM, &signal_handler);
        signal(SIGINT, &signal_handler);

        /*
         * Fork the process and have the parent exit. If the process was started
         * from a shell, this returns control to the user. Forking a new process is
         * also a prerequisite for the subsequent call to setsid().
         */
        if (pid_t pid = fork()) {
           if (pid > 0) {
               exit(0);
           }
           else {
               printf("First fork failed: %m");
               return 1;
           }
        }

        setsid();
        //chdir("/");
        umask(0);

        /* A second fork ensures the process cannot acquire a controlling terminal. */
        if (pid_t pid = fork()) {
           if (pid > 0) {
               exit(0);
           }
           else {
               printf("Second fork failed: %m");
               return 1;
           }
        }

        /*
        * Close the standard streams. This decouples the daemon from the terminal
        * that started it.
        */
        close(0);
        close(1);
        close(2);

        /* We don't want the daemon to have any standard input. */
        if (open("/dev/null", O_RDONLY) < 0) {
           return 1;
        }

        /* Send standard output to a log file. */
        const char* output = "/var/log/smart_home";
        const int flags = O_WRONLY | O_CREAT | O_APPEND;
        const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
        if (open(output, flags, mode) < 0) {
            return 2;
        }

        /* Also send standard error to the same log file. */
        if (dup(1) < 0) {
            logger(SM_LOG_ERR, "Unable to dup output descriptor: %m");
            return 1;
        }

        /* Parsing config.xml configuration file. */
        parse_config(XML_CONFIG_NAME);

        /* Parsing user_access.xml user configuration file. */
        //user_xml_doc.parse_file(USER_XML_CONFIG_NAME);

        /* Parsing tty.conf serial config file */
        parse_serial_config(TTY_CONF_NAME);

        /* init fingerprint */
        //fp_init();

        init_gsm();

        init_and_start_core();

        /* Initialize UDP/TCP servers on 2323/2324 ports. */
        init_udp_server();
        logger(SM_LOG_INFO,"Successfully initialized UDP server on port 2323.\n");

        init_tcp_server();
        logger(SM_LOG_INFO,"Successfully initialized TCP server on port 2324.\n");

        logger(SM_LOG_INFO, "Smart Home server started");

        for(;;){
            sleep(300);
        }

        logger(SM_LOG_INFO, "Smart Home server stopped"); 
    }
    catch (std::exception& e) {
        logger(SM_LOG_ERR, "Exception: %s", e.what());
    }
}
